/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-08-30     ZeroFree     the first version
 */

#include <rtthread.h>
#include <rtdevice.h>
#include <wlan_mgnt.h>
#include <wlan_prot.h>
#include <wlan_cfg.h>
#include <fal.h>

#include "drv_wlan.h"
#include "drv_gpio.h"

#include "string.h"

#include "spi_drv.h"
#include "trace.h"
#include "netdev_api.h"
#include "common.h"
#include "commands.h"
#include "platform_wrapper.h"

#define DRV_DEBUG
#define LOG_TAG              "drv.wlan"
#include <drv_log.h>

#ifdef BSP_USING_WIFI

//#define ETH_RX_DUMP
//#define ETH_TX_DUMP
//#define MINI_DUMP

#ifdef RT_USING_PM
void wiced_platform_keep_awake(void)
{
    rt_pm_request(PM_SLEEP_MODE_NONE);
}

void wiced_platform_let_sleep(void)
{
    rt_pm_release(PM_SLEEP_MODE_NONE);
}
#endif

struct drv_wifi
{
    struct rt_wlan_device wlan;
	esp_hosted_control_config_t config;
    struct network_handle *nethandle;
    rt_wlan_mode_t mode;
};

#if defined(ETH_RX_DUMP) ||  defined(ETH_TX_DUMP)
static void packet_dump(const char *msg, const void *ptr, rt_uint32_t len)
{
    rt_uint32_t j;
    rt_uint8_t *p = (rt_uint8_t *)ptr;
    rt_kprintf("%s %d byte\n", msg, len);

#ifdef MINI_DUMP
    return;
#endif

    for (j = 0; j < len; j++)
    {
        if ((j % 8) == 0)
        {
            rt_kprintf("  ");
        }

        if ((j % 16) == 0)
        {
            rt_kprintf("\r\n");
        }
        rt_kprintf("%02x ", *p ++);
    }

    rt_kprintf("\n\n");
}
#endif /* dump */

#if 0

static void wm_wlan_client_event(u8 *mac, enum tls_wifi_client_event_type event)
{
    struct rt_wlan_buff buff;
    struct rt_wlan_info sta;
    rt_memcpy(sta.bssid, mac, MAX_ADDR_LEN);
    buff.data = &sta;
    buff.len = sizeof(sta);
    if (WM_WIFI_CLIENT_EVENT_ONLINE == event)
    {
        rt_wlan_dev_indicate_event_handle(wifi_ap.wlan, RT_WLAN_DEV_EVT_AP_ASSOCIATED, &buff);
    }
    else
    {
        rt_wlan_dev_indicate_event_handle(wifi_ap.wlan, RT_WLAN_DEV_EVT_AP_DISASSOCIATED, &buff);
    }
}

static void wm_wlan_promisc_dataframe_callback(u8 *data, u32 data_len)
{
    rt_wlan_dev_promisc_handler(wifi_sta.wlan, data, data_len);
}


/**
  * @brief Net rx callback
  * @param  net_handle - network handle
  * @retval None
  */
static void net_rx_callback(struct network_handle *net_handle)
{
	struct pbuf *rx_buffer = NULL;    
    struct drv_wifi *dev = (struct drv_wifi *)rt_device_find(RT_WLAN_DEVICE_STA_NAME);
    
	rx_buffer = network_read(net_handle, 0);

	if (rx_buffer) {
		rt_wlan_dev_report_data(&dev->wlan, rx_buffer->payload, rx_buffer->len);
        
#if defined(ETH_RX_DUMP)
    packet_dump("Rx", rx_buffer->payload, rx_buffer->len);
#endif
        
		free(rx_buffer->payload);
		rx_buffer->payload = NULL;
		free(rx_buffer);
		rx_buffer = NULL;
	}
}
#endif

/**
  * @brief Station mode rx callback
  * @param  net_handle - station network handle
  * @retval None
  */
static void sta_rx_callback(struct network_handle *net_handle)
{
	struct pbuf *rx_buffer = NULL;    
    struct drv_wifi *dev = (struct drv_wifi *)rt_device_find(RT_WLAN_DEVICE_STA_NAME);
    
	rx_buffer = network_read(net_handle, 0);

	if (rx_buffer) {
		rt_wlan_dev_report_data(&dev->wlan, rx_buffer->payload, rx_buffer->len);
        
#if defined(ETH_RX_DUMP)
    packet_dump("Rx", rx_buffer->payload, rx_buffer->len);
#endif
		
		free(rx_buffer);
		rx_buffer = NULL;
	}
}

/**
  * @brief Softap mode rx callback
  * @param  net_handle - Softap network handle
  * @retval None
  */
static void ap_rx_callback(struct network_handle *net_handle)
{
	struct pbuf *rx_buffer = NULL;
    struct drv_wifi *dev = (struct drv_wifi *)rt_device_find(RT_WLAN_DEVICE_AP_NAME);
    
	rx_buffer = network_read(net_handle, 0);

	if (rx_buffer) {
		rt_wlan_dev_report_data(&dev->wlan, rx_buffer->payload, rx_buffer->len);
		free(rx_buffer);
		rx_buffer = NULL;
	}
}

static rt_err_t drv_wlan_init(struct rt_wlan_device *wlan)
{
    static int inited = 0;
    if (inited)
        return RT_EOK;

    inited = 1;
    return RT_EOK;
}

static rt_err_t drv_wlan_mode(struct rt_wlan_device *wlan, rt_wlan_mode_t mode)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;

    dev->mode = mode;
    
    return RT_EOK;
}

static rt_err_t drv_wlan_scan(struct rt_wlan_device *wlan, struct rt_scan_info *scan_info)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    struct rt_wlan_info wlan_info;
    struct rt_wlan_buff buff;
    
    int count;
    char* token = RT_NULL;
    rt_uint8_t *ptr = RT_NULL;
	esp_hosted_wifi_scanlist_t* list = wifi_ap_scan_list(&count);
    
	if (count && list)
    {
        for (int i=0; i<count; i++) 
        {
            rt_memset(&wlan_info, 0, sizeof(wlan_info));
            wlan_info.ssid.len = rt_strlen((char *)list[i].ssid);
            rt_memcpy(wlan_info.ssid.val, list[i].ssid, wlan_info.ssid.len);
            
            ptr = wlan_info.bssid;
            token = strtok((char *)list[i].bssid, (const char *)":");
            while( token != NULL )
            {
                * ptr++ = strtol(token, NULL, 16);
                token = strtok(NULL, ":");
            }
            
            if (wlan_info.ssid.len)
                wlan_info.hidden = 1;
            else
                wlan_info.hidden = 0;

            wlan_info.channel = (rt_int16_t)list[i].channel;
            wlan_info.rssi = list[i].rssi;

            wlan_info.datarate = 54 * 1000000;

            wlan_info.band = RT_802_11_BAND_2_4GHZ;
            
            switch (list[i].encryption_mode)
            {
            case WIFI_AUTH_OPEN:
                wlan_info.security = SECURITY_OPEN;
                break;
            case WIFI_AUTH_WEP:
                wlan_info.security = SECURITY_WEP_PSK;
                break;
            case WIFI_AUTH_WPA_PSK:
                wlan_info.security = SECURITY_WPA_TKIP_PSK;
                break;
            case WIFI_AUTH_WPA2_PSK:
                wlan_info.security = SECURITY_WPA_AES_PSK;
                break;
            case WIFI_AUTH_WPA_WPA2_PSK:
                wlan_info.security = SECURITY_WEP_SHARED;
                break;
            case WIFI_AUTH_WPA2_ENTERPRISE:
                wlan_info.security = SECURITY_WPA2_AES_PSK;
                break;
            case WIFI_AUTH_WPA3_PSK:
                wlan_info.security = SECURITY_WPS_OPEN;
                break;
            case WIFI_AUTH_WPA2_WPA3_PSK:
                wlan_info.security = SECURITY_WPS_SECURE;
                break;        
            default:
                wlan_info.security = SECURITY_UNKNOWN;
                break;
            }
            buff.data = &wlan_info;
            buff.len = sizeof(wlan_info);
            rt_wlan_dev_indicate_event_handle(&dev->wlan, RT_WLAN_DEV_EVT_SCAN_REPORT, &buff);
        }
    }
    
    if (list) {
        free(list);
        list = NULL;
    }
    rt_wlan_dev_indicate_event_handle(&dev->wlan, RT_WLAN_DEV_EVT_SCAN_DONE, RT_NULL);   
    
    return RT_EOK;
}

static rt_err_t drv_wlan_join(struct rt_wlan_device *wlan, struct rt_sta_info *sta_info)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    
	rt_memcpy((char* )&dev->config.station.ssid, (const char *)sta_info->ssid.val, sta_info->ssid.len);
	rt_memcpy((char* )&dev->config.station.pwd, (const char *)sta_info->key.val, sta_info->key.len);
    rt_sprintf((char* )&dev->config.station.bssid, "%2X:%2X:%2X:%2X:%2X:%2X", sta_info->bssid[0], sta_info->bssid[1], \
    sta_info->bssid[2], sta_info->bssid[3], sta_info->bssid[4], sta_info->bssid[5]);
	dev->config.station.is_wpa3_supported = RT_FALSE;
    
	if (wifi_set_ap_config(dev->config)) {
		rt_wlan_dev_indicate_event_handle(&dev->wlan, RT_WLAN_DEV_EVT_CONNECT_FAIL, RT_NULL);
		return -RT_ERROR;
	} else {
		rt_wlan_dev_indicate_event_handle(&dev->wlan, RT_WLAN_DEV_EVT_CONNECT, RT_NULL);
		dev->nethandle = network_open(STA_INTERFACE, sta_rx_callback);
        RT_ASSERT(dev->nethandle != RT_NULL);
	}
    
    return RT_EOK;
}

static rt_err_t drv_wlan_softap(struct rt_wlan_device *wlan, struct rt_ap_info *ap_info)
{
	struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;

	rt_memcpy((char* )&dev->config.station.ssid, (const char *)ap_info->ssid.val, ap_info->ssid.len);
	rt_memcpy((char* )&dev->config.station.pwd, (const char *)ap_info->key.val, ap_info->key.len);
	dev->config.station.is_wpa3_supported = RT_FALSE;
    dev->config.softap.channel = ap_info->channel;
	dev->config.softap.max_connections = 4;
    dev->config.softap.ssid_hidden       = ap_info->hidden ? 1 : 0;
	dev->config.softap.bandwidth         = WIFI_BW_HT40;
	switch (ap_info->security)  /* only support wpa2-psk and open */
    {
    case SECURITY_WEP_PSK:
        dev->config.softap.encryption_mode = WIFI_AUTH_OPEN;
        break;
    case SECURITY_WPA_TKIP_PSK:
        dev->config.softap.encryption_mode = WIFI_AUTH_OPEN;
        break;
    case SECURITY_WPA_AES_PSK:
        dev->config.softap.encryption_mode = WIFI_AUTH_WPA_PSK;
        break;
    case SECURITY_WPA2_TKIP_PSK:
    case SECURITY_WPA2_AES_PSK:
    case SECURITY_WPA2_MIXED_PSK:
        dev->config.softap.encryption_mode = WIFI_AUTH_WPA2_PSK;
        break;
    default:
        dev->config.softap.encryption_mode = WIFI_AUTH_OPEN;
        break;
    }

	if (wifi_set_softap_config(dev->config)) {
		printf("Failed to start softAP \n\r");
		return RT_ERROR;
	} else {
		rt_wlan_dev_indicate_event_handle(&dev->wlan, RT_WLAN_DEV_EVT_AP_START, RT_NULL);
		dev->nethandle = network_open(SOFTAP_INTERFACE, ap_rx_callback);
        RT_ASSERT(dev->nethandle != RT_NULL);
	}
    
	return RT_EOK;
}

static rt_err_t drv_wlan_disconnect(struct rt_wlan_device *wlan)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    
    wifi_disconnect_ap();
    rt_wlan_dev_indicate_event_handle(&dev->wlan, RT_WLAN_DEV_EVT_DISCONNECT, RT_NULL);
    
    return RT_EOK;
}

static rt_err_t drv_wlan_ap_stop(struct rt_wlan_device *wlan)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    
    wifi_stop_softap();
    rt_wlan_dev_indicate_event_handle(&dev->wlan, RT_WLAN_DEV_EVT_AP_STOP, RT_NULL);

    return RT_EOK;
}

static rt_err_t drv_wlan_ap_deauth(struct rt_wlan_device *wlan, rt_uint8_t mac[])
{
    LOG_D("F:%s L:%d", __FUNCTION__, __LINE__);
//    struct tls_wifi_hdr_mac_t machdr;
//    struct tls_wifi_tx_rate_t tx;
//    rt_memset(&machdr, 0, sizeof(machdr));
//    rt_memcpy(machdr.da_addr, mac, MAX_ADDR_LEN);
//    rt_memcpy(machdr.sa_addr, hostapd_get_mac(), MAX_ADDR_LEN);
//    rt_memcpy(machdr.bssid, hostapd_get_mac(), MAX_ADDR_LEN);
//    rt_memset(&tx, 0, sizeof(tx));
//    tx.tx_rate = WM_WIFI_TX_RATEIDX_1M;
//    tx.tx_gain = tls_wifi_get_tx_gain_max(WM_WIFI_TX_RATEIDX_1M);
//    unsigned short reason = WLAN_REASON_UNSPECIFIED;/* htons */
//    int ret = tls_wifi_send_mgmt(WM_WIFI_MGMT_TYPE_DEAUTH, &machdr, (u8 *)&reason, sizeof(reason), &tx);

    return 0;//(0 == ret) ? RT_EOK : RT_ERROR;
}

static rt_err_t drv_wlan_scan_stop(struct rt_wlan_device *wlan)
{
    LOG_D("F:%s L:%d", __FUNCTION__, __LINE__);

    return RT_EOK;
}

static int drv_wlan_get_rssi(struct rt_wlan_device *wlan)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    
    wifi_get_ap_config(&dev->config);
    
    return dev->config.station.rssi;
}

static rt_err_t drv_wlan_set_powersave(struct rt_wlan_device *wlan, int level)
{
	return wifi_set_power_save_mode(level);
}

static int drv_wlan_get_powersave(struct rt_wlan_device *wlan)
{
	int power_save;
    
	wifi_get_power_save_mode(&power_save);
    
    return power_save;
}

static rt_err_t drv_wlan_cfg_promisc(struct rt_wlan_device *wlan, rt_bool_t start)
{
    LOG_D("F:%s L:%d", __FUNCTION__, __LINE__);

    if (RT_TRUE == start)
    {
//        tls_wifi_set_listen_mode(1);
//        tls_wifi_data_recv_cb_register(wm_wlan_promisc_dataframe_callback);
    }
    else
    {
//        tls_wifi_set_listen_mode(0);
//        tls_wifi_data_recv_cb_register(RT_NULL);
    }

    return RT_EOK;
}

static rt_err_t drv_wlan_cfg_filter(struct rt_wlan_device *wlan, struct rt_wlan_filter *filter)
{
    LOG_D("F:%s L:%d", __FUNCTION__, __LINE__);

    return RT_EINVAL;/* not support */
}

static rt_err_t drv_wlan_set_channel(struct rt_wlan_device *wlan, int channel)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    
    if(dev->mode == RT_WLAN_STATION)   
    {
        dev->config.station.channel = channel;
        return wifi_set_ap_config(dev->config);   
         
    }        
	else if(dev->mode == RT_WLAN_AP)
	{
        dev->config.softap.channel = channel;
        return wifi_set_softap_config(dev->config);   
    }

    return -RT_ERROR;
}

static int drv_wlan_get_channel(struct rt_wlan_device *wlan)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    
    if(dev->mode == RT_WLAN_STATION)   
    {
        wifi_get_ap_config(&dev->config);   
        return dev->config.station.channel;
    }        
	else if(dev->mode == RT_WLAN_AP)
	{
        wifi_get_softap_config(&dev->config);   
        return dev->config.softap.channel;
    }
    
    return -RT_ERROR;
}

static rt_err_t drv_wlan_set_country(struct rt_wlan_device *wlan, rt_country_code_t country_code)
{
    LOG_D("F:%s L:%d", __FUNCTION__, __LINE__);
	wifi_set_max_tx_power(country_code);

    return RT_EOK;
}

static rt_country_code_t drv_wlan_get_country(struct rt_wlan_device *wlan)
{
    LOG_D("F:%s L:%d", __FUNCTION__, __LINE__);
	rt_int8_t region;
	wifi_get_curr_tx_power(&region);
	
    return (rt_country_code_t)region;
}

static rt_err_t drv_wlan_set_mac(struct rt_wlan_device *wlan, rt_uint8_t mac[])
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    char macstr[20];
    
    rt_sprintf(macstr, "%2X:%2X:%2X:%2X:%2X:%2X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
    if(dev->mode == RT_WLAN_STATION)   
		return wifi_set_mac(WIFI_MODE_STA, (char *)macstr);
	else if(dev->mode == RT_WLAN_AP)
		return wifi_set_mac(WIFI_MODE_AP, (char *)macstr);
	
    return -RT_ERROR;
}

static rt_err_t drv_wlan_get_mac(struct rt_wlan_device *wlan, rt_uint8_t mac[])
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    char macstr[20];
    char* token = RT_NULL;
    rt_uint8_t *ptr = RT_NULL;
    rt_err_t ret = -RT_ERROR;
    
    if(dev->mode == RT_WLAN_STATION)
		ret = wifi_get_mac(WIFI_MODE_STA, (char *)macstr);
    else if(dev->mode == RT_WLAN_AP)
		ret = wifi_get_mac(WIFI_MODE_AP, (char *)macstr);

    ptr = &mac[0];
    token = strtok((char *)macstr, (const char *)":");
    while( token != NULL )
    {
        * ptr++ = strtol(token, NULL, 16);
        token = strtok(NULL, ":");
    }
            
    return ret;
}

static int drv_wlan_recv(struct rt_wlan_device *wlan, void *buff, int len)
{
    return RT_EOK;
}

static int drv_wlan_send(struct rt_wlan_device *wlan, void *buff, int len)
{
    struct drv_wifi *dev = (struct drv_wifi *)wlan->user_data;
    struct pbuf *buffer = RT_NULL;

#if ETH_PAD_SIZE
    pbuf_header(p, -ETH_PAD_SIZE);    /* Drop the padding word */
#endif

#if defined(ETH_TX_DUMP)
    packet_dump("Tx", buff, len);
#endif

	buffer = malloc(sizeof(struct pbuf));
	RT_ASSERT(buffer != RT_NULL);

	buffer->payload = malloc(len);
	RT_ASSERT(buffer->payload != RT_NULL);

	buffer->len = len;
	rt_memcpy(buffer->payload, buff, len);
    if(network_write(dev->nethandle, buffer) == STM_FAIL)
	{	
		if(buffer->payload)
			free(buffer->payload);
		if(buffer)
			free(buffer);
	}

#if ETH_PAD_SIZE
    pbuf_header(p, ETH_PAD_SIZE);    /* Reclaim the padding word */
#endif

    return RT_EOK;
}

static const struct rt_wlan_dev_ops ops =
{
    .wlan_init              = drv_wlan_init,
    .wlan_mode              = drv_wlan_mode,
    .wlan_scan              = drv_wlan_scan,
    .wlan_join              = drv_wlan_join,
    .wlan_softap            = drv_wlan_softap,
    .wlan_disconnect        = drv_wlan_disconnect,
    .wlan_ap_stop           = drv_wlan_ap_stop,
    .wlan_ap_deauth         = drv_wlan_ap_deauth,
    .wlan_scan_stop         = drv_wlan_scan_stop,
    .wlan_get_rssi          = drv_wlan_get_rssi,
    .wlan_set_powersave     = drv_wlan_set_powersave,
    .wlan_get_powersave     = drv_wlan_get_powersave,
    .wlan_cfg_promisc       = drv_wlan_cfg_promisc,
    .wlan_cfg_filter        = drv_wlan_cfg_filter,
    .wlan_set_channel       = drv_wlan_set_channel,
    .wlan_get_channel       = drv_wlan_get_channel,
    .wlan_set_country       = drv_wlan_set_country,
    .wlan_get_country       = drv_wlan_get_country,
    .wlan_set_mac           = drv_wlan_set_mac,
    .wlan_get_mac           = drv_wlan_get_mac,
    .wlan_recv              = drv_wlan_recv,
    .wlan_send              = drv_wlan_send,
};

static void wlan_mode_work(struct rt_work *work, void *work_data)
{
    reset_slave();
    
    rt_wlan_set_mode(RT_WLAN_DEVICE_STA_NAME, RT_WLAN_STATION);
    rt_wlan_set_mode(RT_WLAN_DEVICE_AP_NAME, RT_WLAN_AP);
}

int rt_hw_wlan_init(void)
{
	rt_err_t ret = -RT_ERROR;
	struct drv_wifi *wifi_sta = RT_NULL;
    struct drv_wifi *wifi_ap = RT_NULL;
    struct rt_work *work = RT_NULL;

    wifi_sta = (struct drv_wifi *)rt_malloc(sizeof(struct drv_wifi));
    RT_ASSERT(wifi_sta != RT_NULL);
    rt_memset(wifi_sta, 0, sizeof(struct drv_wifi));
    
    wifi_ap = (struct drv_wifi *)rt_malloc(sizeof(struct drv_wifi));
    RT_ASSERT(wifi_ap != RT_NULL);
    rt_memset(wifi_ap, 0, sizeof(struct drv_wifi));
    
    work = (struct rt_work *)rt_malloc(sizeof(struct rt_work));
    RT_ASSERT(work != RT_NULL);
    
    /* Init esp32 interface */
	network_init();
	stm_spi_init();
    control_path_platform_init();
    
    ret = rt_wlan_dev_register(&wifi_sta->wlan, RT_WLAN_DEVICE_STA_NAME, &ops, 0, wifi_sta);
    ret |= rt_wlan_dev_register(&wifi_ap->wlan, RT_WLAN_DEVICE_AP_NAME, &ops, 0, wifi_ap);
    
    /* set wifi work mode */
    rt_work_init(work, wlan_mode_work, RT_NULL);
    rt_work_submit(work, 0);
    
    return ret;
}
INIT_DEVICE_EXPORT(rt_hw_wlan_init);
//   wifi join wifi Mzx542505270
#endif
